package mop.model.project;

import java.util.LinkedList;
import java.util.List;

import mop.model.task.Task;
import mop.model.task.TaskInterface;
import mop.exception.IllegalDescriptionException;
import mop.exception.IllegalDuplicationException;
import mop.exception.IllegalIdException;
import mop.model.*;

/**
 * Class for Project.
 * 
 * @invar 	Description is valid.
 *  		| hasValidDescription()
 *  
 * @invar All tasks we're binded to are binded to us
 * 	|for(Task t : tasks)
 * 	|	t.getProject().equals(this)
 *  
 * @author	MOP Team 7
 */
public class Project extends AbstractModelWithDescription implements ProjectInterface
{
	
	/**
	 * Project constructor.
	 * 
	 * @param	projectDatabase
	 * 			DataBase object containing all projects.
	 * @param	description
	 * 			The description of this object.
	 * @effect	(Tries to) set the description.
	 * 			| setDescription(description)
	 * @post	A new, empty list of assigned tasks is made.
	 * 			| tasks.size() == 0
	 * @post	The project is added to the database.
	 * 			| projectDatabase.contains(this)
	 * @throws  IllegalDescriptionException
	 * 			Thrown when the Project description is invalid.
	 * @throws	IllegalDuplicationException 
	 * 			Thrown when a Project with the same description already exists in the database.
	 */
	public Project(DataBase<Project> projectDatabase, String description) 
		throws IllegalDescriptionException, IllegalDuplicationException
	{
		super(description);
		this.projectDatabase = projectDatabase;
	
		// Set empty list of tasks.
		tasks = new LinkedList<Task>();
		
		// Add to database.
		projectDatabase.add(this);
	}
	
	/***************************************************************************
	 * 						Model interface
	 ***************************************************************************/
	
	/**
	 * @see ModelInterface#getId()
	 */
	public String getId() 
	{
		return getDescription();
	}
	
	/**
	 * get a reference to the interface
	 * @return this
	 */
	public ProjectInterface getInterface()
	{
		return this;
	}
	
	/***************************************************************************
	 * 							Project database
	 ***************************************************************************/
	
	private DataBase<Project> projectDatabase;
	
	/***************************************************************************
	 * 							Project description
	 ***************************************************************************/
	 
	 /**
	  * Checks whether a project with the given description already exists in our database.
	  * 
	  * @param 		String description
	  * 			Project description
	  * @return		True if a project with the given description already exists in our database,
	  * 			otherwise false of course.
	  */
	 public boolean isDuplicate(String description)
	 {
		 for(Project p : projectDatabase.fetchAll())
		 {
			 if(p.getDescription().equals(description))
				 return true;
		 }
		 return false;
	 }
	 	
	 /**
	  * Sets the description.
	  * 
	  * @param 		description
	  *  			The description of the project.
	  * @post 		The description is set.
	  * 			| this.getDescription().equals(description)
	  * @throws 	IllegalDescriptionException 
	  *       		Thrown when the description is invalid.
	  *       		| ! isValidDescription(description) 
	  * @throws		IllegalDuplicationException 
	  * 			Thrown when a project with the given description already exists.
	  * 			| isDuplicate(description)
	  */
	 protected void setDescription(String description) 
	 	throws IllegalDescriptionException, IllegalDuplicationException
	 {
		 if(!isValidDescription(description))
			 throw new IllegalDescriptionException(description);
		 
		 if(isDuplicate(description))
			 throw new IllegalDuplicationException(description);
		 
		 this.description = description;
	 }
	

	/***************************************************************************
	 * 						Tasks
	 ***************************************************************************/
	 
	 /**
	  * Returns a list of interfaces to the tasks associated with this project.
	  * 
	  * @return A list of task interfaces.
	  */
	 public List<TaskInterface> getTaskList()
	 {
		 List<TaskInterface> r = new LinkedList<TaskInterface>();
		 r.addAll(this.tasks);
		 return r;
	 }
	
	 /**
	 * Adds a task to the project.
	 * 
	 * @param 	task
	 * 			The task to be added.
	 * @effect	Task is added to the list of tasks
	 * 			| tasks.add(task)
	 */
	public void addTask(Task task)
	{
		if(!tasks.contains(task))
		{
			tasks.add(task);
		}
	}
	
	/**
	 * Remove task from project.
	 * 
	 * @param 	task
	 * 			Task to be removed
	 * @effect	Task is removed from task list
	 * 			| tasks.remove(task)
	 */
	public void removeTask(Task task)
	{
		tasks.remove(task);
	}
	
	/**
	 * Variable to hold the tasks associated with the project
	 */
	private List<Task> tasks = new LinkedList<Task>();
	
	/***************************************************************************
	 * 						       Removal
	 ***************************************************************************/
	
	/**
	 * Destroys a project and its tasks.
	 * 
	 * @effect	Removes all tasks in the list.
	 * @throws	IllegalIdException
	 * 			Thrown when this project does not exist anymore in the database.
	 */
	public void remove() 
		throws IllegalIdException
	{
		/* (this does not work, because the method Task.remove(int) changes the tasks list)
		for (Task t : tasks)
			t.remove(0); */
		
		// remove tasks that are assigned to the project
		while (tasks.size() > 0)
			tasks.get(0).remove(0); // removes task + dependencies of other tasks to the task
		
		this.tasks = null;
		
		// remove project from database
		this.projectDatabase.remove(this);	
	}
	
	
	/***************************************************************************
	 * 						Java object interface
	 ***************************************************************************/
	
	/**
	 * @see 	Object#equals(Object)
	 */
	public boolean equals(Object obj)
	{
		if(obj.getClass() == getClass())
		{
			if(((Project) obj).getId().equals(this.getId()))
				return true;
		}
		return false;
	}
	
	/**
	 * @see 	Object#toString()
	 */
	public String toString()
	{
		return getId();
	}
}